#include <iostream>
#include <string>
#include <functional>
#include <map>
using namespace std;
/*
观察者模式的本质是：
提供函数的过程：
B（观察者）将自己的处理函数提供给A（被观察者）
A会将这些函数记录下来（通常存储在容器中）
2. 触发机制：
当A的某个状态（参数）发生改变时
A会自动调用之前B提供的那些函数
通过这些函数把改变的信息告诉B
*/

#include "NonCopyable.hpp"

template <typename Func>
class Events : NonCopyable
{
public:
    // 注册观察者，支持右值引用
    int Connect(Func &&f)
    {
        // 右值
        return Assgin(f);
    }

    // 移除观察者
    void Disconnect(int key)
    {
        m_connections.erase(key);
    }

    // 通知所有的观察者
    template <typename... Args>
    void Notify(Args &&...args)
    {
        for (auto &it : m_connections)
        {
            it.second(std::forward<Args>(args)...);
        }
    }

private:
    // 保存观察者并分配观察者的编号
    template <typename F>
    int Assgin(F &&f)
    {
        int k = m_observerId++;
        m_connections.emplace(k, std::forward<F>(f));
        return k;
    }

    int m_observerId = 0;              // 观察者对应的编号
    std::map<int, Func> m_connections; // 观察者列表
};

// 测试观察者模式

#include <iostream>
#include <functional>

// 定义一个事件，处理函数类型为 void(int)
Events<std::function<void(int)>>
    myEvent;

// 观察者函数
void Observer1(int value)
{
    std::cout << "Observer1: " << value << std::endl;
}

class Observer2
{
public:
    void onNotify(int value)
    {
        std::cout << "Observer2: " << value << std::endl;
    }
};

int main()
{
    // 注册函数观察者
    int id1 = myEvent.Connect(Observer1);

    // 注册lambda观察者
    int id2 = myEvent.Connect([](int value)
                              { std::cout << "Lambda observer: " << value << std::endl; });

    // 注册成员函数观察者
    Observer2 obs2;
    int id3 = myEvent.Connect(std::bind(&Observer2::onNotify, &obs2, std::placeholders::_1));

    // 增加一个const的connect
    int id4 = myEvent.Connect(Observer1); // utf 测试

    // 使用常量函数作为观察者111
    const auto constObserver = [](int value)
    {
        std::cout << "Const Lambda observer: " << value << std::endl;
    };
    int id5 = myEvent.Connect(constObserver);

    // 在增加一个

    //  触发事件，所有观察者都会收到通知
    myEvent.Notify(42);

    // 移除观察者
    myEvent.Disconnect(id2);

    // 再次触发事件
    myEvent.Notify(100);

    return 0;
}
